package com.github.cakin.shiro.chapter5.hash.credentials;

import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

import java.util.concurrent.atomic.AtomicInteger;

import static com.github.cakin.shiro.chapter5.hash.constant.Constant.NUM5;

/**
 * @className: RetryLimitHashedCredentialsMatcher
 * @description: 密码重试次数限制
 * @date: 2020/5/19
 * @author: cakin
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    /**
     * 声明缓存
     */
    private Ehcache passwordRetryCache;

    public RetryLimitHashedCredentialsMatcher() {
        // 创建一个缓存管理器
        CacheManager cacheManager = CacheManager.newInstance(CacheManager.class.getClassLoader().getResource("ehcache.xml"));
        // 获得一个缓存对象
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    /**
     * 功能描述：实现密码验证功能逻辑
     *
     * @param token 用户传入token
     * @return info 系统存储的认证信息
     * @author cakin
     * @date 2020/5/19
     */
    @Override
    public boolean doCredentialsMatch( AuthenticationToken token, AuthenticationInfo info ) {
        // 获得身份信息
        String username = (String) token.getPrincipal();
        // 获取缓存数据
        Element element = passwordRetryCache.get(username);
        if (element == null) {
            // 针对username，创建一个缓存对象，用于计数
            element = new Element(username, new AtomicInteger(0));
            passwordRetryCache.put(element);
        }
        // 获取缓存对象的值
        AtomicInteger retryCount = (AtomicInteger) element.getObjectValue();
        // 次数超过5次，抛出异常，incrementAndGet 负责加1计数和取出
        if (retryCount.incrementAndGet() > NUM5) {
            throw new ExcessiveAttemptsException();
        }
        // 小于5次，才允许进行密码验证
        boolean matches = super.doCredentialsMatch(token, info);
        // 密码验证成功，清缓存的数据，并返回匹配成功
        if (matches) {
            //clear retry count
            passwordRetryCache.remove(username);
        }
        return matches;
    }
}
